package cn.edu.hit;

import cn.edu.hit.core.Document;
import cn.edu.hit.core.conf.ConfigureLoader;
import cn.edu.hit.kg.NeoDriver;
import cn.edu.hit.kg.data.GraphData;
import cn.edu.hit.kg.data.NodeImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.cli.*;
import org.neo4j.driver.v1.Transaction;
import org.neo4j.driver.v1.Values;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;

public class App {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        args = new String[]{"-h"};
//        args = new String[]{"-e","-p","D:\\Yu-hsin Wang\\Desktop\\知识库展示\\graph.json"};

        Options options = new Options();
//        options.addOption("h","help",false,"打印此帮助信息");

        OptionGroup group1 = new OptionGroup();
        group1.addOption(new Option("h","help",false,"打印此帮助信息"));
        group1.addOption(new Option("i","import",false,"从JSON文件导入到Neo4J"));
        group1.addOption(new Option("e","export",false,"从Neo4J导出为JSON文件"));
        group1.setRequired(true);
        options.addOptionGroup(group1);

        OptionGroup optionGroup = new OptionGroup();
        optionGroup.addOption(new Option("o","io-only",false,"只导入导出(默认)"));
        optionGroup.addOption(new Option("t","try-merge",true,"尝试合并数据，输出冲突列表"));
        optionGroup.addOption(new Option("m","merge",true,"根据冲突列表，进行数据合并"));
        optionGroup.setRequired(false);
        options.addOptionGroup(optionGroup);

        options.addOption("p","path",true,"导入/导出文件路径");

        Class.forName("cn.edu.hit.kg.Neo4jDataProvider");

        HelpFormatter hf = new HelpFormatter();
        hf.setWidth(110);
        CommandLineParser parser = new DefaultParser();

        try {
            CommandLine commandLine = parser.parse(options, args,true);
            if (commandLine.hasOption('h')) {
                // 打印使用帮助
                hf.printHelp("java -jar data-exporter.jar -e | -h | -i   [-m <arg> | -o | -t <arg>]  [-p <arg>] [ID1 [ID2 [ID3] …]]",null,options, " ID1,ID2,ID3,…    指定导出的故障ID,用空格分开，不指定时默认导出全部",false);
                return;
            }

            String path = commandLine.getOptionValue('p');
            if (path==null || path.trim().length()==0){
                System.out.println("导入/导出文件路径不可为空");
                return;
            }

            List<String> additionalArgs = commandLine.getArgList();

            if (commandLine.hasOption('i')){
                System.out.println("正在从 "+path + " 导入数据，请稍候……");
                doImport(path,additionalArgs);
                System.out.println("导入完毕！");
            }

            if (commandLine.hasOption('e')){
                System.out.println("正在导出到 "+path+" ,请稍候……");
                doExport(path,additionalArgs);
                System.out.println("导出完毕！");
            }


        }catch (ParseException e) {
            System.out.println("参数错误！");
            hf.printHelp("java -jar data-exporter.jar -e | -h | -i   [-m <arg> | -o | -t <arg>]  [-p <arg>] [ID1 [ID2 [ID3] …]]",null,options, " ID1,ID2,ID3,…    指定导出的故障ID,用空格分开，不指定时默认导出全部",false);
        }catch (Exception e){
            e.printStackTrace();
        }
//        doExport("G:\\故障诊断\\graph20181219.json");
//        testImport(new URL("file:/G:/故障诊断/graph.json"));
    }

    private static void doExport(String path, List<String> additionalArgs) throws IOException {
        ConfigureLoader configureLoader = ConfigureLoader.getInstance();
        Document document = configureLoader.getBaseConfigure();
        document.put("useNeo4j",true); // 覆盖配置文件项，强制使用Neo4J作为数据源

        GraphData graphData;
        GraphData.reloadData();
        graphData = GraphData.getInstance();

        OutputStream outputStream = Files.newOutputStream(Paths.get(path), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
        if (additionalArgs==null || additionalArgs.size()==0){
            graphData.saveTo(outputStream);
        }else {
            //TODO 筛选并重建一个新的Graph
//            GraphStruct graphStruct = new GraphStruct();
//            graphData.getIdSetByLabel("Fault")
//                    .stream()
//                    .map(graphData::getById)
//                    .filter(node -> node.id())
        }

        outputStream.flush();
        outputStream.close();
    }

    private static void doImport(String path, List<String> additionalArgs) throws Exception{
        ObjectMapper objectMapper = new ObjectMapper();
        GraphStruct data = objectMapper.readValue(Files.newBufferedReader(Paths.get(path)), GraphStruct.class);
        System.out.println(data.nodes);
        System.out.println(data.edges);
        System.out.println(data.labelGroups);
//        Map<Long,Long> idMap = new HashMap<>(); //（文件）原ID --> (Neo4J)新ID
        try (NeoDriver driver = new NeoDriver()){
           driver.executeVoid(session -> {
               session.run("match p=()-[]-() delete p");
               session.run("match (n) delete n");
               Transaction transaction = session.beginTransaction();
               data.nodes.values().forEach(node -> {
                   StringBuffer cyper = new StringBuffer("create (n) ");
                   for (String label : node.getLabels()){
                       cyper.append(" set n:").append(label);
                   }
                   cyper.append(" set n.file_id="+node.getId());
                   Map<String,Object> valueMap = new HashMap<>();
                   node.asMap().forEach((k,v)->{
                       valueMap.put(k,Values.value(v));
                       cyper.append(" set n.").append(k).append("=$").append(k);
                   });
                   transaction.run(cyper.toString(),valueMap);
               });
//               transaction.success();
               data.edges.forEach((fromId,targetSet)->{
                   targetSet.forEach(targetId->{
                       transaction.run("match (n),(m) where n.file_id="+fromId+" and m.file_id="+targetId+" create (n)-[r:hasEdge]->(m)");
                   });
               });
               transaction.success();
               transaction.close();
           });
        }
    }

    public static class GraphStruct{
        public Map<Long, NodeImpl> nodes = new HashMap<>();
        public Map<Long, Set<Long>> edges = new HashMap<>();
        public Map<String, Set<Long>> labelGroups = new HashMap<>();
    }
}
